//-------------------------------------------------------------------
//	Copyright (c) 2013  Zhirnov Andrey
//	This file is part of the "Noise Library" project.
//	See copyright notice in "linNoiseCL.h".
//-------------------------------------------------------------------

#pragma once

#include "../CSource.h"

namespace Noise
{

	//
	// Function Source for output color (interface)
	//

	class ISourceOutput : public CSource
	{
	public:
		NATIVE(
		virtual fvec4 NativeFunction4_32 (const fvec3 &vPoint) = 0;
		virtual dvec4 NativeFunction4_64 (const dvec3 &vPoint) = 0;
		)
		virtual EPixelFormat GetFormat () const = 0;
	};

	SHARED_POINTER( ISourceOutput );



	
	//
	// Function Source for output value
	//

	class CSourceOutput : public ISourceOutput
	{
	// types
	protected:
		typedef CSourceOutput	Self;


	// variables
	protected:
		CSourcePtr		_red,
						_green,
						_blue,
						_alpha;
		
		static const
			ESource		_sourceType = RENDER_OUTPUT;


	// methods
	public:
		CSourceOutput ()
		{}

		~CSourceOutput ()
		{}

		ESourceState GetState () const
		{
			return (_red.IsNotNull() or _green.IsNotNull() or _blue.IsNotNull() or _alpha.IsNotNull()) ?
					SOURCE_COMPLETE : UNCOMPLETE_ATTACHMENT_SOURCE;
		}
		
		virtual void Delete ()
		{
			_red	= null;
			_green	= null;
			_blue	= null;
			_alpha	= null;
		}

		virtual EResult GetInteger (ESourceQuery type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case SOURCE_STATE :
					v[0] = GetState();
					return OK;

				case SOURCE_TYPE :
					v[0] = _sourceType;
					return OK;

				case SOURCES_COUNT : {
					int	r_count = 0,
						g_count = 0,
						b_count = 0,
						a_count = 0;

					if ( _red.IsNotNull() )		_red->GetInteger( type, &r_count, 1 );
					if ( _green.IsNotNull() )	_green->GetInteger( type, &g_count, 1 );
					if ( _blue.IsNotNull() )	_blue->GetInteger( type, &b_count, 1 );
					if ( _alpha.IsNotNull() )	_alpha->GetInteger( type, &a_count, 1 );

					v[0] = r_count + g_count + b_count + a_count + 1;
					return OK;
				}

				case INPUT_SOURCES :
					v[0] = 4;
					return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult SetArg (EArgument type, CSourcePtr src)
		{
			switch ( type )
			{
				case SOURCE_0 :	_red   = src;	return OK;
				case SOURCE_1 :	_green = src;	return OK;
				case SOURCE_2 :	_blue  = src;	return OK;
				case SOURCE_3 :	_alpha = src;	return OK; 
			}
			return INVALID_ENUM;
		}

		virtual EResult GetArg (EArgument type, CSourcePtr &src) const
		{
			switch ( type )
			{
				case SOURCE_0 :	src = _red;		return OK;
				case SOURCE_1 :	src = _green;	return OK;
				case SOURCE_2 :	src = _blue;	return OK;
				case SOURCE_3 :	src = _alpha;	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual CSourcePtr Clone () const override
		{
			return new Self();
		}
		
		virtual void PreBuild (const TPreBuildData &data) override
		{
			if ( _red.IsNotNull() )		data.Push( _red.ptr() );
			if ( _green.IsNotNull() )	data.Push( _green.ptr() );
			if ( _blue.IsNotNull() )	data.Push( _blue.ptr() );
			if ( _alpha.IsNotNull() )	data.Push( _alpha.ptr() );
		}

		virtual bool BuildFunction (TFunctionData &data) override
		{
			CSource::BuildFunctionHeader( data.source, _id ) << " ( (real4)( ";

			if ( _red.IsNull() )	data.source << "0.0";
			else					CSource::BuildFunctionCall( data.source, _red->GetSourceId() );
			data.source << ", ";

			if ( _green.IsNull() )	data.source << "0.0";
			else					CSource::BuildFunctionCall( data.source, _green->GetSourceId() );
			data.source << ", ";

			if ( _blue.IsNull() )	data.source << "0.0";
			else					CSource::BuildFunctionCall( data.source, _blue->GetSourceId() );
			data.source << ", ";
			
			if ( _alpha.IsNull() )	data.source << "0.0";
			else					CSource::BuildFunctionCall( data.source, _alpha->GetSourceId() );
			data.source << " ) )\n";

			return true;
		}
		
		NATIVE(
		virtual float32 NativeFunction32 (const fvec3 &vPoint) override
		{
			WARNING( "this function is not supported, use NativeFunction4" );
			return 0;
		}

		virtual float64 NativeFunction64 (const dvec3 &vPoint) override
		{
			WARNING( "this function is not supported, use NativeFunction4" );
			return 0;
		}

		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		TVec<Real,4> NativeFunction4 (const TVec<Real,3> &vPoint)
		{
			typedef TVec<Real,4>	Real4;
			return Real4( _red.IsNotNull()   ? (_red.ptr()->*NativeFunction)( vPoint )   : Real(0.0),
						  _green.IsNotNull() ? (_green.ptr()->*NativeFunction)( vPoint ) : Real(0.0),
						  _blue.IsNotNull()  ? (_blue.ptr()->*NativeFunction)( vPoint )  : Real(0.0),
						  _alpha.IsNotNull() ? (_alpha.ptr()->*NativeFunction)( vPoint ) : Real(0.0) );
		}
		
		fvec4 NativeFunction4_32 (const fvec3 &vPoint) override
		{
			return NativeFunction4< float32, &CSource::NativeFunction32 >( vPoint );
		}
		
		dvec4 NativeFunction4_64 (const dvec3 &vPoint) override
		{
			return NativeFunction4< float64, &CSource::NativeFunction64 >( vPoint );
		})

		EPixelFormat GetFormat () const override
		{
			if ( _alpha.IsNotNull() )	return RGBA;
			if ( _blue.IsNotNull() )	return RGB;
			if ( _green.IsNotNull() )	return RG;
			if ( _red.IsNotNull() )		return R;
			
			return EPixelFormat(0);
		}

		static bool CreateSource (CSourcePtr &src, CContext *context, ESource type)
		{
			ASSERT( type == _sourceType );

			src = new Self();
			return true;
		}
	};

}	// Noise